package com.odontosis.service;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.List;

import org.hibernate.Session;
import org.hibernate.Transaction;

import com.odontosis.as.OdontosisApplicationService;
import com.odontosis.compare.ComparadorPagamento;
import com.odontosis.dao.ReciboDAO;
import com.odontosis.entidade.FormaPagamento;
import com.odontosis.entidade.Pagamento;
import com.odontosis.entidade.Recibo;
import com.odontosis.entidade.ReciboDetalhe;
import com.odontosis.entidade.TipoRecibo;
import com.odontosis.entidade.Usuario;
import com.odontosis.util.HibernateUtil;

public class ReciboService extends OdontosisApplicationService<Recibo> {

	private final ReciboDAO reciboDAO = new ReciboDAO();
	
	public ReciboService() throws Exception{
		super(Recibo.class, new ReciboDAO());
	}
	
	public Collection<Recibo> pesquisarPorPaciente(String paciente, Date dataInicial, Date dataFinal,TipoRecibo tipo){
		return reciboDAO.pesquisarPorPaciente(paciente, dataInicial, dataFinal, tipo);
	}
	
	public Collection<Recibo> relatorioPorPacientePeridoTipo(String paciente, Date dataInicial, Date dataFinal,TipoRecibo tipo){
		return reciboDAO.relatorioPorPacientePeridoTipo(paciente, dataInicial, dataFinal, tipo);
	}
	
	public Collection<Recibo> relatorioPagamentosEfetuados(String paciente, Date dataInicial, Date dataFinal,TipoRecibo tipo, Usuario usuario){
		return reciboDAO.relatorioPagamentosEfetuados(paciente, dataInicial, dataFinal, tipo, usuario);
	}
	
	public List<Recibo> buscarCheques(String emitente, String banco, String numeroCheque) {
		return reciboDAO.buscarCheques(emitente, banco, numeroCheque);
	}
	
	public Recibo gravarPagamentoEfetuado(Pagamento pagamento, Double valor, TipoRecibo tipo,	FormaPagamento forma,
			String numeroCheque, String banco,	String nomeEmitente, Usuario usuario) {
		
		HibernateUtil.closeSession();		
		Session session = HibernateUtil.currentSession();
		Transaction transaction = session.beginTransaction();

		Recibo recibo = new Recibo();
		recibo.setUsuario(usuario);
		recibo.setData(new Date());
		recibo.setTipoRecibo(tipo);
		recibo.setFormaPagamento(forma);
		if (forma == FormaPagamento.CHEQUE) {
			recibo.setNumeroCheque(numeroCheque);
			recibo.setBanco(banco);
			recibo.setNomeEmitente(nomeEmitente);
		} else {
			recibo.setNumeroCheque(null);
			recibo.setBanco(null);
			recibo.setNomeEmitente(null);
		}
		recibo.setValido(true);
		recibo.setAtivo(true);
		recibo.setVersao(1);
		recibo.setPagamento(pagamento);
		recibo.setValor(new BigDecimal(valor));
		if(recibo.getValor().doubleValue() > pagamento.getValor().doubleValue()) {
			
			List<Pagamento> listaPagamentosEnvolvidos = obterPagamentosDoRecibo(valor.doubleValue(), pagamento);
			try {
				session.saveOrUpdate(recibo);
				
				
				for (Pagamento pagamentoEnvolvido : listaPagamentosEnvolvidos) {
					
					double valorParcela = 0.0;
					if (valor.doubleValue() >= pagamentoEnvolvido.getValor().doubleValue() ) {
						valorParcela = pagamentoEnvolvido.getValor().doubleValue();
						pagamentoEnvolvido.setValor(0.0);
						pagamentoEnvolvido.setPago(true);
						session.saveOrUpdate(pagamentoEnvolvido);
					} else {
						valorParcela = valor.doubleValue();
						Double diferenca = pagamentoEnvolvido.getValor().doubleValue() - valor;
						pagamentoEnvolvido.setValor(diferenca.doubleValue());
						session.saveOrUpdate(pagamentoEnvolvido);
					}
					
					ReciboDetalhe reciboDetalhe = new ReciboDetalhe();
					reciboDetalhe.setAtivo(true);
					reciboDetalhe.setPagamento(pagamentoEnvolvido);
					reciboDetalhe.setRecibo(recibo);
					reciboDetalhe.setValor(new BigDecimal(valorParcela));
					session.saveOrUpdate(reciboDetalhe);
					
					valor -= valorParcela;
				}
				
				//incluído o commit da transação que causava erro ao efetuar pagamentos
				transaction.commit();
				session.flush();
				
			} catch (Exception e) {
				transaction.rollback();
				e.printStackTrace();
				throw new RuntimeException("Erro ao salvar recibo e atualizar Pagamento com Valor superior a Parcela");
			}
			
		} else {
			try {
				
				session.saveOrUpdate(recibo);
	
				if (pagamento.getValor().doubleValue() == valor.doubleValue()) {
					pagamento.setValor(0.0);
					pagamento.setPago(true);
					session.saveOrUpdate(pagamento);
				} else {
					Double diferenca = pagamento.getValor().doubleValue() - valor;
					pagamento.setValor(diferenca.doubleValue());
					session.saveOrUpdate(pagamento);
					
				}
				transaction.commit();
				//System.out.println("valor após salvar : " + pagamento.getValor());
				session.flush();
			} catch (Exception e) {
				transaction.rollback();
				e.printStackTrace();
				throw new RuntimeException("Erro ao salvar recibo e atualizar Pagamento");
			}
		}
		return recibo;
	}
	
	private List<Pagamento> obterPagamentosDoRecibo(double valor, Pagamento pagamento) {
		if(valor <= pagamento.getValor().doubleValue()) {
			throw new RuntimeException("[O valor do recibo deve ser maior que o do pagamento, para efetuar o pagamento posterior.]");
		}
		List<Pagamento> pagamentos = pagamento.getServico().getListaPagamentos();
		List<Pagamento> retorno = new ArrayList<Pagamento>();
		Collections.sort(pagamentos, new ComparadorPagamento());
		Calendar dataPagamento = Calendar.getInstance();
		dataPagamento.set(pagamento.getAnoVencimento(), pagamento.getMesVencimento(), 1);
		for (Pagamento pagamento2 : pagamentos) {
			
			Calendar dataComparado = Calendar.getInstance();
			dataComparado.set(pagamento2.getAnoVencimento(), pagamento2.getMesVencimento(), 1);
			
			if(dataComparado.getTimeInMillis() >= dataPagamento.getTimeInMillis() || pagamento.equals(pagamento2)) {
				if(!pagamento2.isPago() && pagamento2.isAtivo() && pagamento2.getValor().doubleValue() > 0.0) {
					valor -= pagamento2.getValor().doubleValue();
					retorno.add(pagamento2);
					if(valor <= 0) {
						break;
					}
				}
			}
		}
		
		return retorno;
	}
	
	public String buscarCpf(){
		return reciboDAO.buscarCpf();
	}
	
	public String buscarCnpj(){
		return reciboDAO.buscarCnpj();
	}
	
	public String buscarCidade(){
		return reciboDAO.buscarCidade();
	}
	
	public String buscarBairro(){
		return reciboDAO.buscarBairro();
	}
	
	public String buscarEnd(){
		return reciboDAO.buscarEnd();
	}
	public String buscarEndereco(){
		return buscarEnd() + "/" + buscarBairro() + "-" + buscarCidade(); 
	}
	
	public String buscarDentista(){
		return reciboDAO.buscarDentista();
	}
	
	public String buscarCRO(){
		return reciboDAO.buscarCRO();
	}
	
	public String buscarClinica(){
		return reciboDAO.buscarClinica();
	}
	
}
